package com.rem.chat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.rem.chat.common.config.AppConfig;
import com.rem.chat.common.constants.Constants;
import com.rem.chat.common.errcode.ServiceErrorCodeConstants;
import com.rem.chat.common.exception.ServiceException;
import com.rem.chat.common.utils.BeanTransUtil;
import com.rem.chat.mapper.AppUpdateMapper;
import com.rem.chat.mapper.UserInfoMapper;
import com.rem.chat.pojo.dataobject.AppUpdateDO;
import com.rem.chat.pojo.dataobject.UserInfoDO;
import com.rem.chat.pojo.request.PageRequest;
import com.rem.chat.pojo.request.UpdateAddRequest;
import com.rem.chat.pojo.request.UpdateModifyRequest;
import com.rem.chat.pojo.request.UpdatePostRequest;
import com.rem.chat.pojo.response.CheckVersionResponse;
import com.rem.chat.pojo.response.LoadAllUpdateResponse;
import com.rem.chat.service.UpdateService;
import com.rem.chat.service.dto.CheckVersionDTO;
import com.rem.chat.service.enums.AppUpdateFileTypeEnum;
import com.rem.chat.service.enums.AppUpdateStatusEnum;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * @author: Themberfue
 * @date: 2025/6/11 12:56
 * @description:
 */
@Service
public class UpdateServiceImpl implements UpdateService {

    @Resource
    AppUpdateMapper appUpdateMapper;

    @Resource
    UserInfoMapper userInfoMapper;

    @Resource
    AppConfig appConfig;

    @Override
    public LoadAllUpdateResponse loadAllUpdate(PageRequest param) {
        // Step 1. 校验参数
        if (null == param) {
            param = new PageRequest();
        }

        // Step 2. 查询所有的用户数量
        Long total = appUpdateMapper.selectCount(new QueryWrapper<>());

        // Step 3. 进行分页查询
        Page<AppUpdateDO> page = new Page<>(param.getCurrentPage(), param.getPageSize());
        QueryWrapper<AppUpdateDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .orderByDesc(AppUpdateDO::getVersionId);
        List<LoadAllUpdateResponse.UpdateInfo> updateInfos = appUpdateMapper.selectList(page, queryWrapper)
                .stream()
                .map(BeanTransUtil::transToUpdateInfo)
                .toList();

        return new LoadAllUpdateResponse(total, updateInfos);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addUpdate(UpdateAddRequest param, MultipartFile file) throws IOException {
        // Step 1. 校验文件类型
        AppUpdateFileTypeEnum typeEnum = AppUpdateFileTypeEnum.getByStatus(param.getFileType());
        if (null == typeEnum) {
            throw new ServiceException(ServiceErrorCodeConstants.FILE_TYPE_WRONG);
        }

        // Step 2. 校验版本号是否唯一
        QueryWrapper<AppUpdateDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(AppUpdateDO::getVersion, param.getVersion());
        AppUpdateDO appUpdate = appUpdateMapper.selectOne(queryWrapper);
        if (null != appUpdate) {
            throw new ServiceException(ServiceErrorCodeConstants.VERSION_ID_IS_EXISTS);
        }

        // Step 3. 校验当前版本号是否小过 “最大版本号”
        int versionId = Integer.parseInt(param.getVersion().replace(".", ""));
        queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .orderByDesc(AppUpdateDO::getVersionId);
        List<AppUpdateDO> appUpdateList = appUpdateMapper.selectList(new Page<>(0, 1), queryWrapper);
        if (!appUpdateList.isEmpty() && appUpdateList.get(0).getVersionId()
                .compareTo(versionId) >= 0) {
            throw new ServiceException(ServiceErrorCodeConstants.VERSION_ID_NOT_MAX);
        }

        // Step 4. 判断为何种文件类型
        if (typeEnum.equals(AppUpdateFileTypeEnum.LOCAL)) { // ? 本地
            if (null == file) {
                throw new ServiceException(ServiceErrorCodeConstants.INSTALLATION_NULL);
            }

            // Step 4.1 更新记录到服务器
            AppUpdateDO appUpdateDO = AppUpdateDO.builder()
                    .versionId(versionId)
                    .version(param.getVersion())
                    .updateDesc(param.getUpdateDesc())
                    .status(AppUpdateStatusEnum.INIT.getStatus())
                    .fileType(typeEnum.getStatus())
                    .build();
            appUpdateMapper.insert(appUpdateDO);

            // Step 4.2 将安装包存至服务器
            File targetFileFolder = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER_UPDATE);
            if (!targetFileFolder.exists()) {
                targetFileFolder.mkdirs();
            }

            String updatePath = targetFileFolder.getPath() + "/" + Constants.REM_CHAT + appUpdateDO.getVersion() + Constants.UPDATE_SUFFIX;
            file.transferTo(new File(updatePath));
        } else { // ? 外链
            // Step 4.1 更新记录到服务器
            AppUpdateDO appUpdateDO = AppUpdateDO.builder()
                    .versionId(versionId)
                    .version(param.getVersion())
                    .updateDesc(param.getUpdateDesc())
                    .status(AppUpdateStatusEnum.INIT.getStatus())
                    .fileType(typeEnum.getStatus())
                    .outerLink(param.getOuterLink())
                    .build();
            appUpdateMapper.insert(appUpdateDO);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean modifyUpdate(UpdateModifyRequest param, MultipartFile file) throws IOException {
        // Step 1. 校验文件类型
        AppUpdateFileTypeEnum typeEnum = AppUpdateFileTypeEnum.getByStatus(param.getFileType());
        if (null == typeEnum) {
            throw new ServiceException(ServiceErrorCodeConstants.FILE_TYPE_WRONG);
        }

        // Step 2. 查询更新记录是否存在
        AppUpdateDO appUpdate = appUpdateMapper.selectById(param.getId());
        if (null == appUpdate) {
            throw new ServiceException(ServiceErrorCodeConstants.VERSION_ID_IS_NOT_EXISTS);
        }

        // Step 3. 校验版本号是否唯一
        int versionId = Integer.parseInt(param.getVersion().replace(".", ""));
        QueryWrapper<AppUpdateDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(AppUpdateDO::getVersionId, versionId)
                .ne(AppUpdateDO::getId, param.getId());
        AppUpdateDO tmpAppUpdate = appUpdateMapper.selectOne(queryWrapper);
        if (null != tmpAppUpdate) {
            throw new ServiceException(ServiceErrorCodeConstants.VERSION_ID_IS_EXISTS);
        }

        // Step 4. 查询是否允许修改（只有未发布状态才允许修改）
        if (!appUpdate.getStatus()
                .equals(AppUpdateStatusEnum.INIT.getStatus())) {
            throw new ServiceException(ServiceErrorCodeConstants.VERSION_PUBLISHED);
        }

        // Step 5. 校验当前版本号是否大过 “最大版本号”
        queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .orderByDesc(AppUpdateDO::getVersionId);
        List<AppUpdateDO> appUpdateList = appUpdateMapper.selectList(new Page<>(0, 1), queryWrapper);
        if (!appUpdateList.isEmpty() &&
                !appUpdateList.get(0).getId().equals(param.getId()) &&
                appUpdateList.get(0).getVersionId().compareTo(versionId) < 0) {
            throw new ServiceException(ServiceErrorCodeConstants.VERSION_ID_IS_MAX);
        }

        // Step 6. 判断为何种文件类型
        if (typeEnum.equals(AppUpdateFileTypeEnum.LOCAL)) { // ? 本地
            if (null == file) {
                throw new ServiceException(ServiceErrorCodeConstants.INSTALLATION_NULL);
            }

            // Step 6.1 更新记录到服务器
            AppUpdateDO appUpdateDO = AppUpdateDO.builder()
                    .id(appUpdate.getId())
                    .versionId(versionId)
                    .version(param.getVersion())
                    .updateDesc(param.getUpdateDesc())
                    .fileType(typeEnum.getStatus())
                    .outerLink("")
                    .build();
            appUpdateMapper.updateById(appUpdateDO);

            // Step 6.2 将安装包存至服务器
            File targetFileFolder = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER_UPDATE);
            if (!targetFileFolder.exists()) {
                targetFileFolder.mkdirs();
            }

            String updatePath = targetFileFolder.getPath() + "/" + Constants.REM_CHAT + appUpdateDO.getVersion() + Constants.UPDATE_SUFFIX;
            file.transferTo(new File(updatePath));

            // Step 6.3 检验是否需要删除旧的安装包
            if (!appUpdate.getVersion().equals(param.getVersion())) {
                File tmpFile = new File(targetFileFolder.getPath() + "/" + Constants.REM_CHAT + appUpdate.getVersion() + Constants.UPDATE_SUFFIX);
                if (tmpFile.exists()) {
                    boolean isDelete = tmpFile.delete();
                    if (!isDelete) throw new IOException();
                }
            }
        } else { // ? 外链
            // Step 6.1 更新记录到服务器
            AppUpdateDO appUpdateDO = AppUpdateDO.builder()
                    .id(appUpdate.getId())
                    .versionId(versionId)
                    .version(param.getVersion())
                    .updateDesc(param.getUpdateDesc())
                    .fileType(typeEnum.getStatus())
                    .outerLink(param.getOuterLink())
                    .build();
            appUpdateMapper.updateById(appUpdateDO);

            // Step 6.2 检验是否需要删除旧的安装包
            File targetFileFolder = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER_UPDATE);
            if (!targetFileFolder.exists()) {
                targetFileFolder.mkdirs();
            }

            if (!appUpdate.getFileType().equals(param.getFileType())) {
                File tmpFile = new File(targetFileFolder.getPath() + "/" + Constants.REM_CHAT + appUpdate.getVersion() + Constants.UPDATE_SUFFIX);
                if (tmpFile.exists()) {
                    boolean isDelete = tmpFile.delete();
                    if (!isDelete) throw new IOException();
                }
            }
        }
        return true;
    }

    @Override
    public Boolean delUpdate(Integer id) throws IOException {
        // Step 1. 查询更新记录是否存在
        AppUpdateDO appUpdate = appUpdateMapper.selectById(id);
        if (null == appUpdate) {
            throw new ServiceException(ServiceErrorCodeConstants.VERSION_ID_IS_NOT_EXISTS);
        }

        // Step 2. 查询是否允许删除（只有未发布状态才允许修改）
        if (!appUpdate.getStatus()
                .equals(AppUpdateStatusEnum.INIT.getStatus())) {
            throw new ServiceException(ServiceErrorCodeConstants.VERSION_PUBLISHED);
        }

        // Step 3. 检验是否需要删除安装包
        if (appUpdate.getFileType()
                .equals(AppUpdateFileTypeEnum.LOCAL.getStatus())) {
            String targetFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER_UPDATE;
            File tmpFile = new File(targetFilePath + "/" + Constants.REM_CHAT + appUpdate.getVersion() + Constants.UPDATE_SUFFIX);
            if (tmpFile.exists()) {
                boolean isDelete = tmpFile.delete();
                if (!isDelete) throw new IOException();
            }
        }

        // Step 3. 从数据库中删除
        appUpdateMapper.deleteById(id);

        return true;
    }

    @Override
    public Boolean postUpdate(UpdatePostRequest param) {
        // Step 1. 校验发布状态是否存在
        AppUpdateStatusEnum statusEnum = AppUpdateStatusEnum.getByStatus(param.getStatus());
        if (null == statusEnum) {
            throw new ServiceException(ServiceErrorCodeConstants.UPDATE_STATUS_WRONG);
        }

        //  Step 2. 校验更新记录是否存在
        QueryWrapper<AppUpdateDO> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.lambda().eq(AppUpdateDO::getId, param.getId());
        if (!appUpdateMapper.exists(queryWrapper1)) {
            throw new ServiceException(ServiceErrorCodeConstants.VERSION_ID_IS_NOT_EXISTS);
        }

        // Step 3. 判断为何种发布类型
        if (AppUpdateStatusEnum.GRAYSCALE.getStatus()
                .equals(param.getStatus())) { // ? 灰度发布
            if (!StringUtils.hasLength(param.getGrayscaleUid())) {
                throw new ServiceException(ServiceErrorCodeConstants.GRAYSCALE_ID_NULL);
            }

            // Step 3.1 校验灰度用户是否存在
            QueryWrapper<UserInfoDO> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(UserInfoDO::getUserId, param.getGrayscaleUid());
            if (!userInfoMapper.exists(queryWrapper)) {
                throw new ServiceException(ServiceErrorCodeConstants.GRAYSCALE_ID_NULL);
            }
        }

        if (!AppUpdateStatusEnum.GRAYSCALE.getStatus()
                .equals(param.getStatus())) {
            param.setGrayscaleUid("");
        }

        // Step 4. 更新状态到数据库
        AppUpdateDO appUpdateDO = AppUpdateDO.builder()
                .id(param.getId())
                .status(param.getStatus())
                .grayscaleUid(param.getGrayscaleUid())
                .build();
        appUpdateMapper.updateById(appUpdateDO);

        return true;
    }

    @Override
    public CheckVersionResponse checkVersion(String version, String userId) {
        int versionId = Integer.parseInt(version.replace(".", ""));

        // Step 1. 校验是否需要更新
        CheckVersionDTO versionInfo = appUpdateMapper.selectLatestVersion(versionId, userId);
        if (null == versionInfo) {
            return new CheckVersionResponse(0);
        }

        CheckVersionResponse response = new CheckVersionResponse();
        BeanUtils.copyProperties(versionInfo, response);
        response.setIsLatest(1);
        if (versionInfo.getFileType()
                .equals(AppUpdateFileTypeEnum.LOCAL.getStatus())) {
            // Step 2. 计算文件大小
            File file = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER_UPDATE+
                    Constants.REM_CHAT + versionInfo.getVersion() + Constants.UPDATE_SUFFIX);
            response.setFileSize(file.length());
        }

        return response;
    }
}
